/**
 * Copyright (c) 2018, Alex. All rights reserved.
 */
package com.oschina.bottle.convert;

import com.oschina.bottle.convert.impl.DefaultBeanConverter;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.convert.converter.GenericConverter.ConvertiblePair;
import org.springframework.util.Assert;

/**
 * converter注册类
 *
 * @author <a href="mailto:zhongchao@gegejia.com">zhong</a>
 * @version 1.0 2018/1/19
 * @since 1.0
 */
public class ConverterRegistry {

    private final Map<ConvertiblePair, Converter<?, ?>> CONVERTERS = new ConcurrentHashMap<>();
    private final DefaultBeanConverter<?, ?>                DEFAULT_CONVERTER = new DefaultBeanConverter<>();

    public <T> T convert(Object source, Class<T> targetClass) {
        ConvertiblePair key = new ConvertiblePair(source.getClass(), targetClass);
        Converter converter = CONVERTERS.get(key);

        if(converter == null) {
            converter = DEFAULT_CONVERTER;
        }

        return (T) converter.convert(source, targetClass);
    }

    void registry(Converter<?, ?> converter) {
        Assert.notNull(converter, "this BeanConverter must not be null");
        ConvertiblePair typeInfo = this.getRequiredTypeInfo(converter, Converter.class);
        Assert.notNull(typeInfo,
            "Unable to the determine sourceType <S> and targetType <T> which your ModelConverter<S, T> converts between; declare these generic types.");

        CONVERTERS.put(typeInfo, converter);
    }

    private ConvertiblePair getRequiredTypeInfo(Object converter, Class<?> genericIfc) {
        Class<?>[] args = GenericTypeResolver.resolveTypeArguments(converter.getClass(), genericIfc);
        return (args != null ? new ConvertiblePair(args[0], args[1]) : null);
    }


}
